/**
 * 
 */
package com.jdy.haoduoaiteacher.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.jdy.haoduoaiteacher.intface.OnLightSwitchToggleListener;
import com.jdy.haoduoaiteacher.util.Tools;
import com.ycsj.goldmedalnewconcept.R;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author zp 2016-11-14 下午2:41:20
 */
public class BreathLightSwitch extends View {

  protected int WIDTH;

  protected int HEIGHT;

  private final Paint mPaint;

  private int defaultStrokeColor = Color.argb(255, 152, 152, 152);

  private int switchStrokeColor = Color.argb(255, 255, 255, 255);

  private final float strokeWidth = 5;

  private Path mContourPath;

  private BreathSwitch mBreathSwitch;

  float ANGLE_RANGE;

  private float START_DEGREE = 215;

  private float END_DEGREE = 235;

  private final float MAX_DELTA = (END_DEGREE - START_DEGREE) / 2;

  private float DIAMETER = 32;

  private final int SAMPLES = 20;

  private Region mActiveRegion;

  private float ratio = 1;

  private float RADIUS_1 = 540;

  private float BIGGER_RADIUS = RADIUS_1;

  private float SMALL_RADIUS = BIGGER_RADIUS - DIAMETER;

  private OnLightSwitchToggleListener mOnLightSwitchToggleListener;

  private final static String TAG = "BreathLightSwitch";

  /**
   * @param context
   * @param attrs
   * @param defStyleAttr
   */
  public BreathLightSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    // TODO Auto-generated constructor stub
    mPaint = new Paint();

    mPaint.setAntiAlias(true);

    mPaint.setStyle(Style.FILL_AND_STROKE);

    mPaint.setStrokeWidth(strokeWidth);

    mPaint.setColor(defaultStrokeColor);

    this.ratio = Tools.getPixelRatio(context);

    TypedArray typeArray = context.obtainStyledAttributes(attrs,
        R.styleable.BreathLightSwitch);

    defaultStrokeColor = typeArray.getColor(
        R.styleable.BreathLightSwitch_bls_background_color, defaultStrokeColor);

    switchStrokeColor = typeArray.getColor(
        R.styleable.BreathLightSwitch_bls_highlight_color, switchStrokeColor);

    DIAMETER = typeArray.getFloat(R.styleable.BreathLightSwitch_bls_thickness,
        DIAMETER);

    START_DEGREE = typeArray.getFloat(
        R.styleable.BreathLightSwitch_bls_start_degree, START_DEGREE);

    END_DEGREE = typeArray.getFloat(
        R.styleable.BreathLightSwitch_bls_end_degree, END_DEGREE);

    typeArray.recycle();

    RADIUS_1 *= this.ratio;

    DIAMETER *= this.ratio;

    BIGGER_RADIUS = RADIUS_1;

    SMALL_RADIUS = BIGGER_RADIUS - DIAMETER;

    if (isInEditMode())
      return;

  }

  public void setOnLightSwitchToggleListener(
      OnLightSwitchToggleListener listener) {
    this.mOnLightSwitchToggleListener = listener;
  }

  private void initParameters() {

    mContourPath = getContourPoints(RADIUS_1, SMALL_RADIUS,
        Math.toRadians(START_DEGREE), Math.toRadians(END_DEGREE));

    mActiveRegion = new Region();

    RectF rectF = new RectF(0, 0, WIDTH, HEIGHT);

    Path activePath = getContourPoints(BIGGER_RADIUS + DIAMETER / 2,
        SMALL_RADIUS - DIAMETER / 2, Math.toRadians(START_DEGREE),
        Math.toRadians(END_DEGREE));

    mActiveRegion.setPath(activePath, new Region((int) rectF.left,
        (int) rectF.top, (int) rectF.right, (int) rectF.bottom));

    activePath.computeBounds(rectF, true);

    mBreathSwitch = new BreathSwitch(START_DEGREE);

  }

  /**
   * @param context
   * @param attrs
   */
  public BreathLightSwitch(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    // TODO Auto-generated constructor stub
  }

  /**
   * @param context
   */
  public BreathLightSwitch(Context context) {
    this(context, null);
    // TODO Auto-generated constructor stub
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldW, int oldH) {
    WIDTH = w;

    HEIGHT = h;

    initParameters();
    // mPaint.setShader(new RadialGradient(WIDTH / 2, HEIGHT / 2, WIDTH / 2,
    // Color.TRANSPARENT, Color.BLACK, TileMode.MIRROR));

  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int desiredWidth = (int) (1200 * this.ratio);

    int desiredHeight = (int) (1200 * this.ratio);

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);

    int widthSize = MeasureSpec.getSize(widthMeasureSpec);

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;

    int height;

    // Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
      // Must be this size
      width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
      // Can't be bigger than...
      width = Math.min(desiredWidth, widthSize);
    } else {
      // Be whatever you want
      width = desiredWidth;
    }

    // Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
      // Must be this size
      height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
      // Can't be bigger than...
      height = Math.min(desiredHeight, heightSize);
    } else {
      // Be whatever you want
      height = desiredHeight;
    }

    // MUST CALL THIS
    setMeasuredDimension(width, height);
  }

  private Path getContourPoints(float biggerR, float smallR, double start,
      double end) {

    PointF c = new PointF(WIDTH / 2, HEIGHT / 2);

    // double start = Math.toRadians(START_DEGREE);
    //
    // double end = Math.toRadians(END_DEGREE);

    PointF p1 = getPointByAngle(c, smallR, start);

    PointF p2 = getPointByAngle(c, biggerR, start);

    PointF p12 = new PointF((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);

    PointF p3 = getPointByAngle(c, biggerR, end);

    PointF p4 = getPointByAngle(c, smallR, end);

    PointF p34 = new PointF((p3.x + p4.x) / 2, (p3.y + p4.y) / 2);

    // REF_CENTER_POINT = c;
    //
    // REF_RADIUS = RADIUS_1 - DIAMETER / 2;

    List<PointF> contourList = new ArrayList<PointF>();

    contourList.addAll(Arrays.asList(getPointsByAngleRange(c, biggerR, start,
        end, SAMPLES)));

    contourList.addAll(Arrays.asList(getPointsByAngleRange(p34,
        (biggerR - smallR) / 2, end, end + Math.PI, SAMPLES)));

    contourList.addAll(Arrays.asList(getPointsByAngleRange(c, smallR, end,
        start, SAMPLES)));

    contourList
        .addAll(Arrays.asList(getPointsByAngleRange(p12,
            (biggerR - smallR) / 2, start + Math.PI, start + Math.PI * 2,
            SAMPLES)));

    Path path1 = new Path();

    for (int i = 0; i < contourList.size(); i++) {
      if (i == 0) {
        path1.moveTo(contourList.get(i).x, contourList.get(i).y);
      } else {
        path1.lineTo(contourList.get(i).x, contourList.get(i).y);
      }
    }

    path1.close();

    return path1;
  }

  private PointF getPointByAngle(PointF center, float radius, double rotateAngle) {
    PointF p = new PointF();
    p.x = center.x + radius * (float) Math.cos(rotateAngle);
    p.y = center.y + radius * (float) Math.sin(rotateAngle);
    return p;
  }

  private PointF[] getPointsByAngleRange(PointF center, float radius,
      double start, double end, int samples) {
    double range = end - start;
    double step = range / samples;
    List<PointF> points = new ArrayList<PointF>();
    double c = start;
    if (range > 0) {
      for (; c <= end; c += step) {
        PointF n = getPointByAngle(center, radius, c);
        points.add(n);
      }
    } else {
      for (; c >= end; c += step) {
        PointF n = getPointByAngle(center, radius, c);
        points.add(n);
      }
    }
    return points.toArray(new PointF[points.size()]);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    mPaint.setColor(defaultStrokeColor);

    canvas.drawPath(mContourPath, mPaint);

    mBreathSwitch.draw(canvas);

    invalidate();
  }

  public boolean getSwitchOffState() {
    if (mBreathSwitch != null)
      return mBreathSwitch.getSwitchOffState();
    else
      return false;
  }

  private class BreathSwitch {

    private Path mSwitchPath;

    private final Region mContourRegion;

    private final RectF rectF;

    private final float duration = 300;

    private int timer = 0;

    private final int framerate = 20;

    private boolean animationRunning = false;

    private final Handler canvasHandler = new Handler();

    private boolean mSwitchOff = false;

    private float StartDegree;

    private float STEP = 1;

    private final Runnable runnable = new Runnable() {
      @Override
      public void run() {
        invalidate();
      }
    };

    public boolean getSwitchOffState() {
      return mSwitchOff;
    }

    public BreathSwitch(float startDegree) {

      StartDegree = startDegree;

      mContourRegion = new Region();

      rectF = new RectF(0, 0, WIDTH, HEIGHT);

      mSwitchPath = getContourPoints(RADIUS_1, SMALL_RADIUS,
          Math.toRadians(StartDegree),
          Math.toRadians((START_DEGREE + END_DEGREE) / 2));

      mContourRegion.setPath(mSwitchPath, new Region((int) rectF.left,
          (int) rectF.top, (int) rectF.right, (int) rectF.bottom));

      mSwitchPath.computeBounds(rectF, true);

      STEP = MAX_DELTA / (duration / framerate);

    }

    public boolean contains(int x, int y) {
      return mContourRegion.contains(x, y);
    }

    private void updateSwitchPath() {

      mSwitchPath = getContourPoints(RADIUS_1, SMALL_RADIUS,
          Math.toRadians(StartDegree), Math.toRadians(StartDegree + MAX_DELTA));

      mContourRegion.setPath(mSwitchPath, new Region((int) rectF.left,
          (int) rectF.top, (int) rectF.right, (int) rectF.bottom));

      mSwitchPath.computeBounds(rectF, true);
    }

    public void toggle() {
      animationRunning = true;
      if (mOnLightSwitchToggleListener != null) {
        mOnLightSwitchToggleListener.OnLightSwitchToggle(!mSwitchOff);
      }
    }

    public void draw(Canvas canvas) {

      if (animationRunning) {
        if (duration <= timer * framerate) {
          animationRunning = false;
          mSwitchOff = !mSwitchOff;
          timer = 0;
          invalidate();
          return;
        } else
          canvasHandler.postDelayed(runnable, framerate);
        timer++;

        if (mSwitchOff) {
          StartDegree -= STEP;
        } else {
          StartDegree += STEP;
        }
        updateSwitchPath();
      }

      mPaint.setColor(switchStrokeColor);

      canvas.drawPath(mSwitchPath, mPaint);
    }
  }

  private boolean mSelected = false;

  int xTouch;

  int yTouch;

  @Override
  public boolean onTouchEvent(final MotionEvent event) {
    boolean handled = false;

    // get touch event coordinates and make transparent circle from it
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
      Log.w(TAG, "Move Down");
      xTouch = (int) event.getX(0);
      yTouch = (int) event.getY(0);

      this.mSelected = mActiveRegion.contains(xTouch, yTouch);

      invalidate();
      handled = true;
      break;

    case MotionEvent.ACTION_MOVE:

      Log.w(TAG, "Move");
      // xTouch = (int) event.getX(0);
      // yTouch = (int) event.getY(0);

      invalidate();
      handled = true;
      break;

    case MotionEvent.ACTION_UP:
      Log.w(TAG, "Motion Up");

      // xTouch = (int) event.getX(0);
      // yTouch = (int) event.getY(0);
      // this.mOptionButton.setDiscretePosition(xTouch, yTouch,
      // mOptionAreaList);
      if (this.mSelected) {
        // if (Math.abs(event.getX(0) - xTouch) > 2) {
        this.mBreathSwitch.toggle();
        this.mSelected = false;
        // }
      }
      this.mSelected = false;
      invalidate();
      handled = true;
      break;

    case MotionEvent.ACTION_CANCEL:
      Log.w(TAG, "Motion Cancel");
      xTouch = (int) event.getX(0);
      yTouch = (int) event.getY(0);
      // this.mOptionButton.setDiscretePosition(xTouch, yTouch,
      // mOptionAreaList);
      // this.mSelected = false;
      invalidate();
      handled = true;
      break;

    default:
      // this.mSelected = false;
      break;
    }

    handled = mActiveRegion.contains((int) event.getX(0), (int) event.getY(0));

    return super.onTouchEvent(event) || handled;
  }

}
